package com.luo.d3s.core.util.validation;

import com.luo.d3s.core.exception.ValidateException;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 验证工具类
 *
 * @author luohq
 * @date 2022-11-26
 */
public class Validates {

    /**
     * 符号常量
     */
    private static final String DOT = ".";
    private static final String SEPARATOR_COLON = ": ";
    private static final String SEPARATOR_COMMA = ", ";

    /**
     * Javax 验证器
     */
    private static Validator validator;

    /**
     * 自定义异常转换器(errCode, errMsg -> RuntimeException) -> <br/>
     * 注：若有需要可全局替换词异常转换逻辑
     */
    public static BiFunction<String, String, RuntimeException> errorToExceptionFunc;

    static {
        //初始化验证器
        validator = Validation.buildDefaultValidatorFactory().getValidator();
        //初始异常转换器
        errorToExceptionFunc = (errCode, errMessage) -> Optional.ofNullable(errCode)
                .map(errCodeInner -> new ValidateException(errCode, errMessage))
                .orElse(new ValidateException(errMessage));
    }

    // ================================================================================================
    // ========= Java Validation验证 ===================================================================
    // ================================================================================================

    /**
     * 校验对象
     *
     * @param object 待校验对象
     * @param groups 待校验的组
     */
    public static <T> void validateEntity(T object, Class<?>... groups) {
        Set<ConstraintViolation<T>> constraintViolations = validator.validate(object, groups);
        //不存在验证错误则正常返回
        if (null == constraintViolations || constraintViolations.isEmpty()) {
            return;
        }
        //存在验证错误则抛出验证错误异常
        throw errorToExceptionFunc.apply(null, convertConstraintViolations(constraintViolations));
    }

    /**
     * 设置自定义的Validator实现
     *
     * @param validator 自定义的Validator实现
     */
    public static void setValidator(Validator validator) {
        Validates.validator = validator;
    }

    /**
     * 转换constraintViolation集合异常为错误提示信息
     *
     * @param constraintViolations 验证异常集合
     * @return 错误提示信息
     */
    public static <T> String convertConstraintViolations(Set<ConstraintViolation<T>> constraintViolations) {
        if (null == constraintViolations || constraintViolations.isEmpty()) {
            return null;
        }
        return constraintViolations.stream()
                .map(Validates::convertConstrainViolation)
                .collect(Collectors.joining(SEPARATOR_COMMA));
    }

    /**
     * 转换constraintViolation集合异常为错误提示信息
     *
     * @param constraintViolations 验证异常集合
     * @return 错误提示信息
     */
    public static <T> String convertConstraintViolationsUnknownType(Set<ConstraintViolation<?>> constraintViolations) {
        if (null == constraintViolations || constraintViolations.isEmpty()) {
            return null;
        }
        return constraintViolations.stream()
                .map(Validates::convertConstrainViolation)
                .collect(Collectors.joining(SEPARATOR_COMMA));
    }

    /**
     * 转换constraintViolation异常为错误提示信息
     *
     * @param constraintViolation 验证异常
     * @return错误提示信息
     */
    public static String convertConstrainViolation(ConstraintViolation<?> constraintViolation) {
        String rootBeanName = constraintViolation.getRootBeanClass().getSimpleName();
        String path = constraintViolation.getPropertyPath().toString();
        String errMsg = constraintViolation.getMessage();
        return String.format("%s.%s:%s", rootBeanName, path, errMsg);
    }

    // ================================================================================================
    // =========表达式（true或false）验证=================================================================
    // ================================================================================================

    public static void isTrue(boolean expression, String errCode, String errMessage) {
        if (!Boolean.TRUE.equals(expression)) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void isTrue(boolean expression, String errMessage) {
        isTrue(expression, null, errMessage);
    }

    public static void isTrue(boolean expression) {
        isTrue(expression, "[Assertion failed] Must be true");
    }


    public static void isFalse(boolean expression, String errCode, String errMessage) {
        if (!Boolean.FALSE.equals(expression)) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void isFalse(boolean expression, String errMessage) {
        isFalse(expression, null, errMessage);
    }

    public static void isFalse(boolean expression) {
        isFalse(expression, "[Assertion failed] Must be false");
    }


    // ================================================================================================
    // =========非空（Object, Collection, Map, String）验证==============================================
    // ================================================================================================

    public static void notNull(Object object, String errCode, String errMessage) {
        if (object == null) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void notNull(Object object, String errMessage) {
        notNull(object, null, errMessage);
    }

    public static void notNull(Object object) {
        notNull(object, "[Assertion failed] Must not null");
    }


    public static void notBlank(String str, String errCode, String errMessage) {
        if (str == null || str.trim().length() <= 0) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void notBlank(String str, String errMessage) {
        notBlank(str, null, errMessage);
    }

    public static void notBlank(String str) {
        notBlank(str, "[Assertion failed] Must not blank");
    }


    public static void notEmpty(String str, String errCode, String errMessage) {
        if (str == null || str.isEmpty()) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void notEmpty(String str, String errMessage) {
        notEmpty(str, null, errMessage);
    }

    public static void notEmpty(String str) {
        notEmpty(str, "[Assertion failed] Must not empty");
    }


    public static void notEmpty(Collection<?> collection, String errCode, String errMessage) {
        if (collection == null || collection.isEmpty()) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void notEmpty(Collection<?> collection, String errMessage) {
        notEmpty(collection, null, errMessage);
    }

    public static void notEmpty(Collection<?> collection) {
        notEmpty(collection, "[Assertion failed] Collection must not be empty: it must contain at least 1 element");
    }


    public static void notEmpty(Map<?, ?> map, String errCode, String errMessage) {
        if (map == null || map.isEmpty()) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void notEmpty(Map<?, ?> map, String errMessage) {
        notEmpty(map, null, errMessage);
    }

    public static void notEmpty(Map<?, ?> map) {
        notEmpty(map, "[Assertion failed] Map must not be empty: it must contain at least one entry");
    }


    // ================================================================================================
    // =========数字类型（Short, Integer, Long, Double, Float, BigDecimal）range验证======================
    // ================================================================================================

    public static void range(Short num, Short min, Short max, String errCode, String errMessage) {
        Boolean notInRange = (null != num && (num < min || num > max));
        if (notInRange) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void range(Short num, Short min, Short max, String errMessage) {
        range(num, min, max, null, errMessage);
    }

    public static void range(Short num, Short min, Short max) {
        range(num, min, max, "[Assertion failed] Must not exceed range");
    }

    public static void min(Short num, Short min, String errCode, String errMessage) {
        if (null != num && num < min) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void min(Short num, Short min, String errMessage) {
        min(num, min, null, errMessage);
    }

    public static void min(Short num, Short min) {
        min(num, min, "[Assertion failed] Must not less than min");
    }

    public static void max(Short num, Short max, String errCode, String errMessage) {
        if (null != num && num > max) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void max(Short num, Short max, String errMessage) {
        max(num, max, null, errMessage);
    }

    public static void max(Short num, Short max) {
        max(num, max, "[Assertion failed] Must not greater than max");
    }


    public static void range(Integer num, Integer min, Integer max, String errCode, String errMessage) {
        Boolean notInRange = (null != num && (num < min || num > max));
        if (notInRange) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void range(Integer num, Integer min, Integer max, String errMessage) {
        range(num, min, max, null, errMessage);
    }

    public static void range(Integer num, Integer min, Integer max) {
        range(num, min, max, "[Assertion failed] Must not exceed range");
    }

    public static void min(Integer num, Integer min, String errCode, String errMessage) {
        if (null != num && num < min) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void min(Integer num, Integer min, String errMessage) {
        min(num, min, null, errMessage);
    }

    public static void min(Integer num, Integer min) {
        min(num, min, "[Assertion failed] Must not less than min");
    }

    public static void max(Integer num, Integer max, String errCode, String errMessage) {
        if (null != num && num > max) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void max(Integer num, Integer max, String errMessage) {
        max(num, max, null, errMessage);
    }

    public static void max(Integer num, Integer max) {
        max(num, max, "[Assertion failed] Must not greater than max");
    }


    public static void range(Long num, Long min, Long max, String errCode, String errMessage) {
        Boolean notInRange = (null != num && (num < min || num > max));
        if (notInRange) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void range(Long num, Long min, Long max, String errMessage) {
        range(num, min, max, null, errMessage);
    }

    public static void range(Long num, Long min, Long max) {
        range(num, min, max, "[Assertion failed] Must not exceed range");
    }


    public static void min(Long num, Long min, String errCode, String errMessage) {
        if (null != num && num < min) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void min(Long num, Long min, String errMessage) {
        min(num, min, null, errMessage);
    }

    public static void min(Long num, Long min) {
        min(num, min, "[Assertion failed] Must not less than min");
    }

    public static void max(Long num, Long max, String errCode, String errMessage) {
        if (null != num && num > max) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void max(Long num, Long max, String errMessage) {
        max(num, max, null, errMessage);
    }

    public static void max(Long num, Long max) {
        max(num, max, "[Assertion failed] Must not greater than max");
    }

    public static void range(Double num, Double min, Double max, String errCode, String errMessage) {
        Boolean notInRange = (null != num && (num < min || num > max));
        if (notInRange) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void range(Double num, Double min, Double max, String errMessage) {
        range(num, min, max, null, errMessage);
    }

    public static void range(Double num, Double min, Double max) {
        range(num, min, max, "[Assertion failed] Must not exceed range");
    }

    public static void min(Double num, Double min, String errCode, String errMessage) {
        if (null != num && num < min) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void min(Double num, Double min, String errMessage) {
        min(num, min, null, errMessage);
    }

    public static void min(Double num, Double min) {
        min(num, min, "[Assertion failed] Must not less than min");
    }

    public static void max(Double num, Double max, String errCode, String errMessage) {
        if (null != num && num > max) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void max(Double num, Double max, String errMessage) {
        max(num, max, null, errMessage);
    }

    public static void max(Double num, Double max) {
        max(num, max, "[Assertion failed] Must not greater than max");
    }

    public static void range(Float num, Float min, Float max, String errCode, String errMessage) {
        Boolean notInRange = (null != num && (num < min || num > max));
        if (notInRange) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void range(Float num, Float min, Float max, String errMessage) {
        range(num, min, max, null, errMessage);
    }

    public static void range(Float num, Float min, Float max) {
        range(num, min, max, "[Assertion failed] Must not exceed range");
    }

    public static void min(Float num, Float min, String errCode, String errMessage) {
        if (null != num && num < min) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void min(Float num, Float min, String errMessage) {
        min(num, min, null, errMessage);
    }

    public static void min(Float num, Float min) {
        min(num, min, "[Assertion failed] Must not less than min");
    }

    public static void max(Float num, Float max, String errCode, String errMessage) {
        if (null != num && num > max) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void max(Float num, Float max, String errMessage) {
        max(num, max, null, errMessage);
    }

    public static void max(Float num, Float max) {
        max(num, max, "[Assertion failed] Must not greater than max");
    }


    public static void range(BigDecimal num, BigDecimal min, BigDecimal max, String errCode, String errMessage) {
        Boolean notInRange = (null != num && (num.compareTo(min) < 0 || num.compareTo(max) > 0));
        if (notInRange) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void range(BigDecimal num, BigDecimal min, BigDecimal max, String errMessage) {
        range(num, min, max, null, errMessage);
    }

    public static void range(BigDecimal num, BigDecimal min, BigDecimal max) {
        range(num, min, max, "[Assertion failed] Must not exceed range");
    }

    public static void min(BigDecimal num, BigDecimal min, String errCode, String errMessage) {
        if (null != num && num.compareTo(min) < 0) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void min(BigDecimal num, BigDecimal min, String errMessage) {
        min(num, min, null, errMessage);
    }

    public static void min(BigDecimal num, BigDecimal min) {
        min(num, min, "[Assertion failed] Must not less than min");
    }

    public static void max(BigDecimal num, BigDecimal max, String errCode, String errMessage) {
        if (null != num && num.compareTo(max) > 0) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void max(BigDecimal num, BigDecimal max, String errMessage) {
        max(num, max, null, errMessage);
    }

    public static void max(BigDecimal num, BigDecimal max) {
        max(num, max, "[Assertion failed] Must not greater than max");
    }


    // ================================================================================================
    // ====================================== 字符串类型长度验证 =========================================
    // ================================================================================================

    public static void length(String text, Integer min, Integer max, String errCode, String errMessage) {
        Boolean notInLength = (null != text && (text.length() < min || text.length() > max));
        if (notInLength) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void length(String text, Integer min, Integer max, String errMessage) {
        length(text, min, max, null, errMessage);
    }

    public static void length(String text, Integer min, Integer max) {
        length(text, min, max, null, "[Assertion failed] Must not exceed length");
    }


    // ================================================================================================
    // ====================================== 字符串类型正则验证 =========================================
    // ================================================================================================

    public static void regex(String text, String regex, String errCode, String errMessage) {
        if (null != text && !Pattern.compile(regex).matcher(text).matches()) {
            throw errorToExceptionFunc.apply(errCode, errMessage);
        }
    }

    public static void regex(String text, String regex, String errMessage) {
        regex(text, regex, null, errMessage);
    }

    public static void regex(String text, String regex) {
        regex(text, "[Assertion failed] Must match regex");
    }

}
